
#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
#endregion

namespace Sample
{
	/// <summary>
	/// This is the main type for your game
	/// </summary>
	public class Game1 : Microsoft.Xna.Framework.Game
	{
		GraphicsDeviceManager graphics;
		ContentManager content;

		Model model;
		ToonEffect toonEffect;
		Texture2D toonTexture;

		Matrix viewMatrix;
		Matrix projectionMatrix;

		Matrix rotationMatrix = Matrix.Identity;


		public Game1()
		{
			graphics = new GraphicsDeviceManager(this);
			content = new ContentManager(Services);
		}


		/// <summary>
		/// Allows the game to perform any initialization it needs to before starting to run.
		/// This is where it can query for any required services and load any non-graphic
		/// related content.  Calling base.Initialize will enumerate through any components
		/// and initialize them as well.
		/// </summary>
		protected override void Initialize()
		{
			this.viewMatrix = Matrix.CreateLookAt(new Vector3(0, 0, 4000), Vector3.Zero, Vector3.Up);
			this.projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f), 4.0f/3.0f, 1.0f, 10000.0f);


			base.Initialize();
		}


		/// <summary>
		/// Load your graphics content.  If loadAllContent is true, you should
		/// load content from both ResourceManagementMode pools.  Otherwise, just
		/// load ResourceManagementMode.Manual content.
		/// </summary>
		/// <param name="loadAllContent">Which type of content to load.</param>
		protected override void LoadGraphicsContent(bool loadAllContent)
		{
			if (loadAllContent)
			{
				this.model = this.content.Load<Model>(@"Model");
				this.toonTexture = this.content.Load<Texture2D>(@"ToonTexture");

				this.toonEffect = new ToonEffect(this.graphics.GraphicsDevice);
				this.toonEffect.ToonTexture = this.toonTexture;
			}

			// TODO: Load any ResourceManagementMode.Manual content
		}


		/// <summary>
		/// Unload your graphics content.  If unloadAllContent is true, you should
		/// unload content from both ResourceManagementMode pools.  Otherwise, just
		/// unload ResourceManagementMode.Manual content.  Manual content will get
		/// Disposed by the GraphicsDevice during a Reset.
		/// </summary>
		/// <param name="unloadAllContent">Which type of content to unload.</param>
		protected override void UnloadGraphicsContent(bool unloadAllContent)
		{
			if (unloadAllContent == true)
			{
				content.Unload();
			}
		}


		/// <summary>
		/// Allows the game to run logic such as updating the world,
		/// checking for collisions, gathering input and playing audio.
		/// </summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
		protected override void Update(GameTime gameTime)
		{
			GamePadState padState = GamePad.GetState(PlayerIndex.One);

			// Allows the default game to exit on Xbox 360 and Windows
			if (padState.Buttons.Back == ButtonState.Pressed)
				this.Exit();

			// Use the four primary buttons to set the toon effect's diffuse to match the button color
			// Notice how the Diffuse is an explicit typesafe property 
			// Without EffectGenerator, you have to use: this.toonEffect.Parameters["Diffuse"].SetValue(...
			if (padState.Buttons.A == ButtonState.Pressed)
				this.toonEffect.Diffuse = Color.Lime.ToVector4();
			else if (padState.Buttons.B == ButtonState.Pressed)
				this.toonEffect.Diffuse = Color.Red.ToVector4();
			else if (padState.Buttons.X == ButtonState.Pressed)
				this.toonEffect.Diffuse = Color.Blue.ToVector4();
			else if (padState.Buttons.Y == ButtonState.Pressed)
				this.toonEffect.Diffuse = Color.Yellow.ToVector4();

			// Use the shoulder buttons to adjust the outline thickness
			// Notice the simple use of ++ and --
			// Contrast this with:
			// toonEffect.Parameters["OutlineThickness"].SetValue(toonEffect.Parameters["OutlineThickness"].GetValueSingle()+1);
			if (padState.Buttons.LeftShoulder == ButtonState.Pressed)
				this.toonEffect.OutlineThickness--;
			if (padState.Buttons.RightShoulder == ButtonState.Pressed)
				this.toonEffect.OutlineThickness++;

			// Use the left joystick to rotate the model
			float rotationFactor = MathHelper.Pi * (float)gameTime.ElapsedGameTime.TotalSeconds;
			rotationMatrix *= Matrix.CreateRotationX(rotationFactor * -padState.ThumbSticks.Left.Y) * Matrix.CreateRotationY(rotationFactor * padState.ThumbSticks.Left.X);

			// Update matricies
			Matrix worldMatrix = rotationMatrix * this.model.Meshes[0].ParentBone.Transform;
			Matrix worldViewMatrix = worldMatrix * this.viewMatrix;
			this.toonEffect.WorldView = worldViewMatrix;
			this.toonEffect.WorldViewProjection = worldViewMatrix * projectionMatrix;


			base.Update(gameTime);
		}


		/// <summary>
		/// This is called when the game should draw itself.
		/// </summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
		protected override void Draw(GameTime gameTime)
		{
			GraphicsDevice device = graphics.GraphicsDevice;
			device.Clear(Color.CornflowerBlue);

			// Draw the mesh using the effect
			this.toonEffect.Begin(SaveStateMode.SaveState);
			foreach (EffectPass pass in this.toonEffect.CurrentTechnique.Passes)
			{
				pass.Begin();

				ModelMesh mesh = this.model.Meshes[0];
				ModelMeshPart meshPart = mesh.MeshParts[0];

				device.VertexDeclaration = meshPart.VertexDeclaration;
				device.Vertices[0].SetSource(mesh.VertexBuffer, meshPart.StreamOffset, meshPart.VertexStride);
				device.Indices = mesh.IndexBuffer;

				device.DrawIndexedPrimitives(PrimitiveType.TriangleList, meshPart.BaseVertex, 0, meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount);

				pass.End();
			}
			this.toonEffect.End();

			// If you implement a processor which replaces the effects,
			// the draw code can be simplified to this.model.Meshes[0].Draw();

			base.Draw(gameTime);
		}
	}
}